package com.cqsd.bean;

import com.cqsd.result.Result;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author caseycheng
 * @date 2023/3/9-17:29
 **/
final public class BeanUtils {
    private static final Object lock = new Object();
    /**
     * 解析bean属性
     */
    private static volatile BeanProperties beanProperties = null;
    /**
     * 解析bean字段并且包含bean to hashMap 互转
     */
    private static volatile BeanField beanField = null;
    /**
     * 对象转换
     */
    private static volatile ObjectMapping objectMapping = null;

    public static BeanProperties getBeanProperties() {
        if (beanProperties == null) {
            synchronized (lock) {
                if (beanProperties == null) {
                    beanProperties = new BeanProperties();
                }
            }
        }
        return beanProperties;
    }

    public static BeanField getBeanField() {
        if (beanField == null) {
            synchronized (lock) {
                if (beanField == null) {
                    beanField = new BeanField();
                }
            }
        }
        return beanField;
    }

    public static ObjectMapping getObjectMapping() {
        if (objectMapping == null) {
            synchronized (lock) {
                if (objectMapping == null) {
                    objectMapping = new ObjectMapping();
                }
            }
        }
        return objectMapping;
    }

    /**
     * 获取读方法
     *
     * @param cla
     * @param propName
     * @return
     */
    public static Result<Method> getReadMethod(Class<?> cla, String propName) {
        return getBeanProperties().getReadMethod(cla, propName);
    }

    /**
     * 获取写方法
     *
     * @param cla
     * @param propName
     * @return
     */
    public static Result<Method> getWriteMethod(Class<?> cla, String propName) {
        return getBeanProperties().getWriteMethod(cla, propName);
    }

    /**
     * 获取可读可写的属性名
     *
     * @param cla
     * @return
     */
    public static List<String> getReadWriteMethodProp(Class<?> cla) {
        return getBeanProperties().getReadWriteMethodProp(cla);
    }

    /**
     * 根据类型获取属性名
     *
     * @param cla
     * @param propertiesType
     * @return
     */
    public static List<String> getProp(Class<?> cla, PropertiesType propertiesType) {
        return getBeanProperties().getProp(cla, propertiesType);
    }

    /**
     * 获取其它方法
     *
     * @param cla
     * @return
     */
    public static List<Method> getOtherMethod(Class<?> cla) {
        return getBeanProperties().getOtherMethod(cla);
    }

    /**
     * 获取所有读方法
     *
     * @param cla
     * @return
     */
    public static List<Method> getReadMethod(Class<?> cla) {
        return getBeanProperties().getReadMethod(cla);
    }

    /**
     * 获取所有写方法
     *
     * @param cla
     * @return
     */
    public static List<Method> getWriteMethod(Class<?> cla) {
        return getBeanProperties().getWriteMethod(cla);
    }

    /**
     * 获取toString方法
     *
     * @param cla
     * @return
     */
    public static Method getToString(Class<?> cla) {
        return getBeanProperties().getToString(cla);
    }

    /**
     * 获取该bean的所有实例方法
     *
     * @param type
     * @return
     */
    public static Map<PropertiesType, List<Method>> getBeanProperties(Class<?> type) {
        return getBeanProperties().getBeanProperties(type);
    }

    /**
     * 往目标对象设置单个值
     *
     * @param bean
     * @param key
     * @param value
     */
    public static void setProp(@NotNull Object bean,@NotNull String key, Object value) {
        getBeanField().setProp(bean, key, value);
    }

    /**
     * 通过map中的值对bean进行写入
     *
     * @param bean
     * @param props
     */
    public static void setProps(Object bean, Map<String, Object> props) {
        getBeanField().setProps(bean, props);
    }

    /**
     * 将Map对象解析为对应Class
     *
     * @param clazz
     * @param prop
     */
    public static <T> T parseMap(Class<T> clazz, Map<String, Object> prop) throws InstantiationException {
        return getBeanField().parseMap(clazz, prop);
    }

    /**
     * 将一个对象转化为Map
     * 注意，这里只是拷贝了引用，如果一个地方发生变动，另一个地方也会发生变动，基元除外
     * Java里面不叫指针，要叫引用，不然会被烦死。
     * @param bean    需要解析的bean对象
     * @param putNull 传入false就会跳过null值
     * @return Map集合
     */
    public static Map<String, Object> toHashMap(Object bean, boolean putNull) {
        return getBeanField().toHashMap(bean, putNull);
    }

    /**
     * 获取子类到父类的所有字段,从这里面出来的字段都是关闭了安全检查的，不用重新关闭一次。
     *
     * @param beanClass 需要获取的类
     * @return 子类到父类的字段列表
     */
    public static List<Field> getBeanField(Class<?> beanClass,Class<?> stopClass) {
        return getBeanField().getBeanField(beanClass,stopClass);
    }

    /**
     * 判断字段是不是静态
     *
     * @param field 一个字段
     * @return 是否为静态字段
     */
    public static boolean isStatic(Field field) {
        return getBeanField().isStatic(field);
    }

    /**
     * 解锁字段
     *
     * @param field 字段
     */
    public static void uncheckedField(Field field) {
        getBeanField().uncheckedField(field);
    }

    /**
     * 对字段集合进行解锁
     *
     * @param fields 一个需要解锁的字段集合
     */
    public static void uncheckedFields(List<Field> fields) {
        getBeanField().uncheckedFields(fields);
    }

    /**
     * 提供一个class对象，该方法会从子类一直扫描到父类
     *
     * @param clazz  需要扫描的对象
     * @param action 提供出去的扫描到的class
     */
    public static void scanClass(Class<?> clazz, Consumer<Class<?>> action) {
        getBeanField().scanClass(clazz, action);
    }

    /**
     * 提供一个class对象，该方法会从子类一直扫描到父类
     *
     * @param startClass 需要扫描的对象
     * @param stopClass  停止扫描的对象
     * @param action     提供出去的扫描到的class
     */
    public static void scanClass(Class<?> startClass, Class<?> stopClass, Consumer<Class<?>> action) {
        getBeanField().scanClass(startClass, stopClass, action);
    }

    /**
     * 将方法数组解析为 方法名和方法的map
     *
     * @param methods
     * @return
     */
    public static Map<String, Method> parseMethodArray(Method[] methods) {
        return getBeanField().parseMethodArray(methods);
    }

    /**
     * 将字段数组解析为字段名和字段的Map
     *
     * @param fields
     * @return
     */
    public static Map<String, Field> parseFieldArray(Field[] fields) {
        return getBeanField().parseFieldArray(fields);
    }

    public static Constructor<?> findAllArgsConstructor(Constructor<?>[] constructors) {
        return getBeanField().findAllArgsConstructor(constructors);
    }

    /**
     * 注册一个转换器
     *
     * @param in       需要转换的类型
     * @param out      转换后的类型
     * @param function 转换的方法
     */
    public static <T, R> void register(Class<T> in, Class<R> out, Function<T, R> function) {
        getObjectMapping().register(in, out, function);
    }

    /**
     * 输入一个需要转换的对象还有一个输出对象的class，返回输出对象
     *
     * @param in  需要转换的对象
     * @param out 转换后的对象
     * @return 转换后的对象
     */
    public static <T, R> R map(T in, Class<R> out) {
        return getObjectMapping().map(in, out);
    }

    /**
     * 获取一个转换器方法
     *
     * @param in  需要转换的对象
     * @param out 转换后的对象方法
     * @param <T>
     * @param <R>
     * @return 转换的方法
     */
    @SuppressWarnings("unchecked")
    public static <T, R> Function<Class<T>, R> getObjMappingFunc(Class<T> in, Class<R> out) {
        return getObjectMapping().getFunc(in, out);
    }

    /**
     * 检查一个对象转换器是否存在
     *
     * @param in  输入对象
     * @param out 输出对象
     * @return 检查是否在这个输入对象下找到转换为输出对象的方法
     */
    public static boolean containClass(Class<?> in, Class<?> out) {
        return getObjectMapping().containClass(in, out);
    }

    public static List<Method> getBeanMethods(Class<?> type) {
        return getBeanProperties().getAllMethods(type);
    }
}
